package com.iretailer.report.easyexcel.listener;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.iretailer.report.common.InternationalMessage;
import com.iretailer.report.easyexcel.data.CameraData;
import com.iretailer.report.error.ImportException;
import com.iretailer.report.model.*;
import com.iretailer.report.service.*;
import com.iretailer.report.util.SecurityUserUtil;
import com.iretailer.report.util.SpringUtils;
import lombok.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Auther Bennie
 * @Date 2020/6/15 15:56
 * @Description
 */
@Slf4j
public class CameraDataListener extends AnalysisEventListener<CameraData> {

    private final String updateUser;
    private final String language;


    private Map<Malls, Collection<Cameras>> dataMap = new HashMap<>();

    @SneakyThrows
    @Override
    @Transactional(rollbackFor = {ImportException.class})
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        for (Malls malls : dataMap.keySet()) {
            Collection<Cameras> camerasCollection = dataMap.get(malls);
            log.debug("导入 【{}】 店铺 数据", malls.getName());
            for (Cameras cameras : camerasCollection) {
                Camera camera = cameras;
                Camera oneCamera = getOneCamera(camera);
                if (oneCamera == null) {
                    camera.setCreateTime(LocalDateTime.now());
                    cameraService.save(camera);
                    camera = getOneCamera(camera);
                } else {
                    if (!oneCamera.getMallId().equals(camera.getMallId())) {
                        throw new ImportException(InternationalMessage.DUPLICATE_DEVICE_ID.getByLanguage(language) + "\t" + oneCamera.getDeviceId());
                    }
                    camera = oneCamera;
                }
                Long cameraId = camera.getId();
                cameras.getCameraZoneList().forEach(cp -> {
                    CameraZone cameraZone = cp.getCameraZone().setCameraId(cameraId);
                    CameraZone oneCameraZone = getOneCameraZone(cameraZone);
                    if (oneCameraZone == null) {
                        cameraZone.setUpdateTime(LocalDateTime.now());
                        cameraZone.setUpdateUser(updateUser);
                        cameraZoneService.save(cameraZone);
                        cameraZone = getOneCameraZone(cameraZone);
                    } else {
                        cameraZone = oneCameraZone;
                    }
                    PhysicalZones physicalZones = cp.getPhysicalZones();
                    PhysicalZones onePhysicalZones = getOnePhysicalZones(physicalZones);
                    if (onePhysicalZones == null) {
                        physicalZones.setUpdateTime(LocalDateTime.now());
                        physicalZones.setUpdateUser(updateUser);
                        physicalzonesService.save(physicalZones);
                        physicalZones = getOnePhysicalZones(physicalZones);
                    } else {
                        physicalZones = onePhysicalZones;
                    }
                    Long cameraZoneId = cameraZone.getId();
                    Long physicalZonesId = physicalZones.getId();
                    PhysicalZoneCameraZone onePhysicalZoneCameraZone = getOnePhysicalZoneCameraZone(cameraZoneId, physicalZonesId);
                    if (onePhysicalZoneCameraZone == null) {
                        PhysicalZoneCameraZone physicalZoneCameraZone = new PhysicalZoneCameraZone().setCameraZoneId(cameraZoneId).setPhysicalzoneId(physicalZonesId).setReverse(1);
                        physicalZoneCameraZoneService.save(physicalZoneCameraZone);
                    }

                });
                // 更新 zone count
                int count = cameraZoneService.count(new QueryWrapper<CameraZone>().lambda().eq(CameraZone::getCameraId, camera.getId()));
                camera.setZoneCount(count);
                camera.setUpdateTime(LocalDateTime.now());
                camera.setUpdateUser(updateUser);
                cameraService.updateById(camera);
            }
        }
        dataMap.forEach((malls, camerasCollection) -> {

        });
    }

    private Camera getOneCamera(Camera camera) {
        return cameraService.getOne(
                new QueryWrapper<Camera>().lambda()
                        .eq(Camera::getDeviceId, camera.getDeviceId())
        );
    }

    private CameraZone getOneCameraZone(CameraZone cameraZone) {
        return cameraZoneService.getOne(
                new QueryWrapper<CameraZone>().lambda()
                        .eq(CameraZone::getCameraId, cameraZone.getCameraId())
                        .eq(CameraZone::getZone, cameraZone.getZone())
                        .eq(CameraZone::getZoneClass, "Count")
        );
    }

    private PhysicalZones getOnePhysicalZones(PhysicalZones physicalZones) {
        return physicalzonesService.getOne(
                new QueryWrapper<PhysicalZones>().lambda()
                        .eq(PhysicalZones::getMallId, physicalZones.getMallId())
                        .eq(PhysicalZones::getZoneTypeId, physicalZones.getZoneTypeId())
                        .eq(PhysicalZones::getName, physicalZones.getName())
        );
    }

    private PhysicalZoneCameraZone getOnePhysicalZoneCameraZone(long cameraZoneId, long physicalZoneId) {
        return physicalZoneCameraZoneService.getOne(
                new QueryWrapper<PhysicalZoneCameraZone>().lambda()
                        .eq(PhysicalZoneCameraZone::getCameraZoneId, cameraZoneId)
                        .eq(PhysicalZoneCameraZone::getPhysicalzoneId, physicalZoneId)
        );
    }


    private final MallsService mallsService;

    private final CameraService cameraService;

    private final CameraZoneService cameraZoneService;

    private final PhysicalzonesService physicalzonesService;

    private final PhysicalZoneCameraZoneService physicalZoneCameraZoneService;

    private final Map<String, List<Categories>> categoriesMap;

    private final HttpServletRequest request;

    public CameraDataListener(HttpServletRequest request) {
        this.request = request;
        this.mallsService = SpringUtils.getBean(MallsService.class);
        this.cameraService = SpringUtils.getBean(CameraService.class);
        this.cameraZoneService = SpringUtils.getBean(CameraZoneService.class);
        CategoriesService categoriesService = SpringUtils.getBean(CategoriesService.class);
        this.physicalzonesService = SpringUtils.getBean(PhysicalzonesService.class);
        this.physicalZoneCameraZoneService = SpringUtils.getBean(PhysicalZoneCameraZoneService.class);
        this.categoriesMap = categoriesService.selectCategoryByKeyNames(
                Arrays.stream(ZoneType.values()).map(ZoneType::name)
                        .collect(Collectors.toList())).stream()
                .collect(Collectors.groupingBy(Categories::getKeyName));
        User principal = SecurityUserUtil.currentUser(request);
        this.language = SecurityUserUtil.userLanguage(request);
        this.updateUser = ("Excel import by " + principal.getName());
    }


    private enum ZoneType {
        Entrance, Corridor, Floor, MallShop, Domain, Face, Shelf;

        static boolean exist(String name) {
            for (ZoneType zoneType : values()) {
                if (zoneType.name().equals(name)) {
                    return true;
                }
            }
            return false;
        }
    }

    @Getter
    private List<CameraData> cameraDataList = new ArrayList<>();

    @SneakyThrows
    @Override
    public void invoke(CameraData cameraData, AnalysisContext analysisContext) {

        String mallName = cameraData.getMallName();
        if (StringUtils.isBlank(mallName)) {
            throw new ImportException(InternationalMessage.EMPTY_MALL_NAME.getByLanguage(language));
        }
        mallName = mallName.trim();

        // 判断相关店铺信息不能为 空
        List<Malls> malls = mallsService.list(new QueryWrapper<Malls>().lambda().eq(Malls::getName, mallName));
        if (malls == null || malls.isEmpty()) {
            throw new ImportException(InternationalMessage.MALL_NOT_FOUND.getByLanguage(language));
        }
        if (malls.size() > 1) {
            throw new ImportException(InternationalMessage.DUPLICATE_SHOP_NAME.getByLanguage(language) + "\t" + mallName);
        }
        Malls mall = malls.get(0);
        if (mall == null) {
            throw new ImportException(InternationalMessage.DUPLICATE_SHOP_NAME.getByLanguage(language) + "\t" + mallName);
        }

        String deviceId = cameraData.getDeviceId();
        if (StringUtils.isBlank(deviceId)) {
            throw new ImportException(InternationalMessage.EMPTY_DEVICE_ID.getByLanguage(language));
        }
        deviceId = deviceId.trim();

        String deviceType = cameraData.getDeviceType();
        if (StringUtils.isBlank(deviceType)) {
            throw new ImportException(InternationalMessage.EMPTY_DEVICE_TYPE.getByLanguage(language));
        }
        deviceType = deviceType.trim();

        String zoneCode = cameraData.getZoneCode();
        if (StringUtils.isBlank(zoneCode)) {
            throw new ImportException(InternationalMessage.EMPTY_ZONE_CODE.getByLanguage(language));
        }

        String physicalName = cameraData.getPhysicalName();
        if (StringUtils.isBlank(physicalName)) {
            throw new ImportException(InternationalMessage.EMPTY_PHYSICAL_NAME.getByLanguage(language));
        }
        physicalName = physicalName.trim();


        String zoneType = cameraData.getZoneType();
        if (StringUtils.isBlank(zoneType)) {
            throw new ImportException(InternationalMessage.EMPTY_ZONE_TYPE.getByLanguage(language));
        }
        zoneType = zoneType.trim();


        // 判断 zone type 是否符合要求
        zoneType = zoneType.split("-")[0].trim();
        boolean exist = ZoneType.exist(zoneType);
        if (!exist) {
            throw new ImportException(InternationalMessage.INVALID_ZONE_TYPE.getByLanguage(language));
        }


        String ranked = cameraData.getRanked();
        if (StringUtils.isBlank(ranked)) {
            throw new ImportException(InternationalMessage.EMPTY_RANKED.getByLanguage(language));
        }
        ranked = ranked.trim();

        String deviceIdAndZone = cameraData.getDeviceIdAndZone();
        if (StringUtils.isBlank(deviceIdAndZone)) {
            throw new ImportException(InternationalMessage.EMPTY_DEVICE_ZONE.getByLanguage(language));
        }
        deviceIdAndZone = deviceIdAndZone.trim();


        String[] splits = deviceIdAndZone.split("#");
        if (splits.length != 2) {
            throw new ImportException(InternationalMessage.EMPTY_DEVICE_ZONE.getByLanguage(language));
        }
        String zone = splits[1].trim();

        Cameras cameras = getCameraByCache(deviceId, mall.getId(), deviceType);

        CameraZone cameraZone = new CameraZone().setZone(zone).setEnabled(1).setZoneClass("Count");
        PhysicalZones physicalZones = getPhysicalZonesByCache(physicalName, mall.getId(),
                categoriesMap.get(zoneType).get(0).getId(), zoneType, Integer.parseInt(ranked));
        CameraZoneAndPhysicalZone cameraZoneAndPhysicalZone = new CameraZoneAndPhysicalZone(cameraZone, physicalZones);
        cameras.getCameraZoneList().add(cameraZoneAndPhysicalZone);
        Collection<Cameras> camerasList = dataMap.get(mall);
        if (camerasList == null) {
            camerasList = new ArrayList<>();
            camerasList.add(cameras);
            dataMap.put(mall, camerasList);
        } else {
            if (!camerasList.contains(cameras)) {
                camerasList.add(cameras);
            }
        }
    }


    private List<Cameras> camerasCache = new ArrayList<>();

    private Cameras getCameraByCache(String deviceId, String mallId, String deviceType) {
        for (Cameras cameras : camerasCache) {
            if (cameras.getDeviceId().equals(deviceId) && cameras.getMallId().equals(mallId) && cameras.getDeviceType().equals(deviceType)) {
                return cameras;
            }
        }
        Cameras cameras = new Cameras();
        cameras.setDeviceId(deviceId);
        cameras.setMallId(mallId);
        cameras.setEnabled(1);
        cameras.setDeviceType(deviceType);
        camerasCache.add(cameras);
        return cameras;
    }


    private List<PhysicalZones> physicalZonesCache = new ArrayList<>();

    private PhysicalZones getPhysicalZonesByCache(String physicalName, String mallId, String zoneTypeId, String zoneTypeName, int ranked) {
        for (PhysicalZones physicalZones : physicalZonesCache) {
            if (physicalZones.getMallId().equals(mallId) &&
                    physicalZones.getZoneTypeId().equals(zoneTypeId) &&
                    physicalZones.getZoneTypeName().equals(zoneTypeName) &&
                    physicalZones.getRanked().equals(ranked) &&
                    physicalZones.getName().equals(physicalName)) {
                return physicalZones;
            }
        }
        PhysicalZones physicalZones = new PhysicalZones().setMallId(mallId)
                .setZoneTypeId(zoneTypeId).setZoneTypeName(zoneTypeName)
                .setEnabled(1).setRanked(ranked).setName(physicalName);
        physicalZonesCache.add(physicalZones);
        return physicalZones;
    }


    @EqualsAndHashCode(callSuper = true)
    @Data
    static class Cameras extends Camera {
        private List<CameraZoneAndPhysicalZone> cameraZoneList = new ArrayList<>();
    }

    @Getter
    @Setter
    @AllArgsConstructor
    static class CameraZoneAndPhysicalZone {

        private CameraZone cameraZone;

        private PhysicalZones physicalZones;


        @Override
        public int hashCode() {
            int code1 = cameraZone == null ? 0 : cameraZone.hashCode();
            int code2 = physicalZones == null ? 0 : physicalZones.hashCode();
            return code1 + code2;
        }

        @Override
        public boolean equals(Object obj) {
            if (obj instanceof CameraZoneAndPhysicalZone) {
                CameraZone cameraZone = ((CameraZoneAndPhysicalZone) obj).getCameraZone();
                if (cameraZone == null) return false;
                PhysicalZones physicalZones = ((CameraZoneAndPhysicalZone) obj).getPhysicalZones();
                if (physicalZones == null) return false;
                return cameraZone.equals(this.cameraZone) && physicalZones.equals(this.physicalZones);
            }
            return false;
        }
    }
}
